package org.springframework.validation.executors;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.executors.impl.*;

/**
 * Stores all discovered named rule executors, and makes them available
 * as validation rule types. Will discover both rule executors which are 
 * provided through the customRuleExecutors property, as well as those which are
 * annotated with @ConfiguredRuleExecutor 
 * @author clark
 *
 */
public class RuleExecutorContainer {
	
	protected Map<String, RuleExecutor> ruleExecutors = new HashMap<String, RuleExecutor>();
	
	protected boolean addDefaultRuleExecutors = true;
	
	@Autowired
	protected BeanFactory beanFactory;
	
	@PostConstruct
	public void discoverAnnotatedRuleExecutors() throws Exception {
		if (addDefaultRuleExecutors) {
			addDefaultRuleExecutors();
		}
		Map<String, Object> ruleExecutorBeans = 
			((ListableBeanFactory) beanFactory).getBeansWithAnnotation(ConfiguredRuleExecutor.class);

		for (String springName : ruleExecutorBeans.keySet()) {
			RuleExecutor ruleExecutor = (RuleExecutor) ruleExecutorBeans.get(springName);
			String ruleName = ruleExecutor.getClass().getAnnotation(ConfiguredRuleExecutor.class).name();
			setCustomRuleExecutor(ruleName, ruleExecutor);
		}
	}

	public void setCustomRuleExecutor(Map<String, RuleExecutor> customRuleExecutors) {
		for (String executorName : customRuleExecutors.keySet()) {
			setCustomRuleExecutor(executorName, customRuleExecutors.get(executorName));
		}
	}
	
	public void setCustomRuleExecutor(String executorName, RuleExecutor ruleExecutor) {
		if (ruleExecutors.containsKey(executorName)) {
			throw new IllegalArgumentException(
				"Implementation for rule name \"" + executorName 
				+ "\" already set to type " 
				+ ruleExecutors.get(executorName).getClass().getCanonicalName());
		} else {
			ruleExecutors.put(executorName, ruleExecutor);
		}
	}
	
	public RuleExecutor getRuleExecutorByName(String executorName) {
		if (ruleExecutors.containsKey(executorName)) {
			return ruleExecutors.get(executorName);
		}
		throw new IllegalArgumentException("No rule executor with name: " + executorName);
	}
	
	protected void addDefaultRuleExecutors()
	{
		setCustomRuleExecutor("alphabetic", new AlphabeticRuleExecutor());
		setCustomRuleExecutor("alphanumeric", new AlphanumericRuleExecutor());
		setCustomRuleExecutor("email", new EmailRuleExecutor());
		setCustomRuleExecutor("maxLength", new MaxLengthRuleExecutor());
		setCustomRuleExecutor("minLength", new MinLengthRuleExecutor());
		setCustomRuleExecutor("numeric", new NumericRuleExecutor());
		setCustomRuleExecutor("required", new RequiredRuleExecutor());
		setCustomRuleExecutor("notEmpty", new RequiredRuleExecutor());
		setCustomRuleExecutor("matches", new MatchesRuleExecutor());
	}

	public void setAddDefaultRuleExecutors(boolean addDefaultRuleExecutors) {
		this.addDefaultRuleExecutors = addDefaultRuleExecutors;
	}
}
